package com.dh.spring.thread;

import com.dh.spring.thread.callable.ThreadCallable;
import com.dh.spring.thread.runnable.ThreadRunnable;
import com.dh.spring.util.LocalTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

/**
 * 线程池功能接口实现类
 *
 *  1、initThreadSize
 *  2、coreThreadSize
 *  3、maxThreadSize
 *  4、BlockQueue
 *  5、ThreadFactory
 *  6、RejectPolicy
 *  7、TimeUnit
 *
 * */
public class CustomDefaultThreadPool<T> implements CustomThreadPool<T> {
    private static final Logger logger = LoggerFactory.getLogger(CustomDefaultThreadPool.class);
    private int initThreadSize = 3, coreThreadSize = 5, maxThreadSize = 8;
    //TODO 线程池是否关闭，true关闭  false开启
    private boolean isShutdown = true;
    private long keepAliveTime = 3000;
    private TimeUnit timeUnit;
    private Thread localThread = null;
    //TODO 任务阻塞队列
    private static CustomTaskBlockQueue customTaskBlockQueue;
    //TODO 线程工厂
    private CustomThreadFactory customThreadFactory;
    //TODO 拒绝策略
    private CustomAbortPolicy customAbortPolicy;
    private String threadPoolName = "Runnable维护线程";
    //TODO 封装用于执行从Callable队列中取出上传文件任务的结合
    private volatile List<FutureTask<Callable>> list = new ArrayList<>();

    public CustomDefaultThreadPool() {
        this(2,
            Runtime.getRuntime().availableProcessors() + 2,
            Runtime.getRuntime().availableProcessors() + 4,
            CustomDefaultTaskBlockQueue.getInstance(),
            CustomDefaultThreadFactory.getInstance(),
            new CustomAbortPolicy.DiscardPolicy(),
            TimeUnit.MILLISECONDS,
            3000,
            null);
    }

    public CustomDefaultThreadPool(Class cls) {
        this(2,
            Runtime.getRuntime().availableProcessors(),
            Runtime.getRuntime().availableProcessors() + 3,
            CustomDefaultTaskBlockQueue.getInstance(),
            CustomDefaultThreadFactory.getInstance(),
            new CustomAbortPolicy.DiscardPolicy(),
            TimeUnit.MILLISECONDS,
            3000,
            cls);
    }

    public CustomDefaultThreadPool(int initThreadSize, int coreThreadSize, int maxThreadSize,
       CustomTaskBlockQueue customTaskBlockQueue, CustomThreadFactory customThreadFactory, CustomAbortPolicy customAbortPolicy, TimeUnit timeUnit, long keepAliveTime, Class cls) {
        this.initThreadSize = initThreadSize;
        this.coreThreadSize = coreThreadSize;
        this.maxThreadSize = maxThreadSize;
        this.customTaskBlockQueue = customTaskBlockQueue;
        this.customThreadFactory = customThreadFactory;
        this.customAbortPolicy = customAbortPolicy;
        this.timeUnit = timeUnit;
        this.keepAliveTime = keepAliveTime;

        System.out.println("<---------------------------------------- 等待接收socket连接的线程池开始初始化 --------------------------------------->");
        System.out.println();




        //TODO 1、重置线程池启停状态为开启
        this.isShutdown = false;
        //logger.info("[ " + LocalTime.formatDate(LocalDateTime.now()) + " ] 线程池CustomDefaultThreadPool实例化完成，且已重置其启动状态 isShutdown = " + isShutdown + " ( " + (isShutdown?"关闭":"开启") + " )......");

        //TODO 2、线程池初始化initThreadCount个数的线程, Callable线程不需要提前创建，提前创建执行无法拿到返回值
        executeInitThread(cls);
        logger.info("[ " + LocalTime.formatDate(LocalDateTime.now()) + " ] 线程池完成 [ " + this.initThreadSize + " ] 个线程的初始化和启动......");

        if(null != cls) {
            this.threadPoolName = "Callable维护线程";
        }

        //TODO 3、创建线程池内部维护线程，执行线程池扩容线程和回收线程任务
        CustomLocalThread customLocalThread = new CustomLocalThread(this.keepAliveTime, this, this.customTaskBlockQueue, this.customThreadFactory, this.threadPoolName);
        localThread = new Thread(customLocalThread, threadPoolName);
        this.localThread.start();
        logger.info("[ " + LocalTime.formatDate(LocalDateTime.now()) + " ] " + this.threadPoolName + " 已开启......");




        System.out.println();
        System.out.println("<---------------------------------------- 等待接收socket连接的线程池开始初始化完成 --------------------------------------->");
        System.out.println();
    }

    private void executeInitThread(Class cls) {
        for (int i = 0; i < this.initThreadSize; i++) {
            this.customThreadFactory.execute(this, this.customTaskBlockQueue, cls);
        }
    }

    @Override
    public int getInitThreadSize() {
        return this.initThreadSize;
    }

    @Override
    public int getCoreThreadSize() {
        return this.coreThreadSize;
    }

    @Override
    public int getMaxThreadSize() {
        return this.maxThreadSize;
    }

    @Override
    public int getActiveThreadSize() {
        return this.customThreadFactory.getActiveThreadSize(this);
    }

    //TODO 提交Runnable任务到线程池  @Param runnable  用户自定义线程执行任务单元(即任务执行逻辑)
    @Override
    public void executeRunnable(Runnable runnable) {
        if(this.isShutdown()) {
            logger.info("[ " + LocalTime.formatDate(LocalDateTime.now()) + " ] 线程池已关闭，提交任务失败......");
            return;
        }

        this.customTaskBlockQueue.offerRunnable(runnable, this,this.customAbortPolicy);
    }

    //TODO 提交Callable上传任务到线程池  @Param callable  用户自定义线程执行任务单元(即任务执行逻辑)
    @Override
    public FutureTask<Callable> executeCallable(Callable callable) {
        if(this.isShutdown()) {
            logger.info("[ " + LocalTime.formatDate(LocalDateTime.now()) + " ] 线程池已关闭，提交任务失败......");
            return null;
        }

        /**
         * @Param Callable callable 此callable用于执行上传文件逻辑
         * @Param List<FutureTask<Callable>> list 此list中的Callable用于执行从上传文件任务所存储的队列中取出上传任务并执行，并获得执行结果
         *
         *  1、判断上传文件Callable任务队列是否为空，为空直接按照默认开启的Callable线程数量执行
         *
         *  2、上传文件Callable任务队列大于默认开启的线程数量，则执行扩容逻辑
         * */
        /*for(FutureTask<Callable> futureTask : this.getList()) {
            if(null != futureTask) {
                futureTask.run();
            }
        }*/

        //TODO 如果添加任务成功，则返回此任务，否则返回null，添加不成功
        FutureTask<Callable> futureTask = new FutureTask<Callable>(callable);
        if(this.customTaskBlockQueue.offerCallable(futureTask, this,this.customAbortPolicy)) {
            return futureTask;
        } else {
            return null;
        }
    }

    //TODO 批量执行所有带返回值得任务
    @Override
    public List<FutureTask<T>> invokeAll(Collection tasks) throws InterruptedException {
        List<FutureTask<T>> resultList = new ArrayList<>();
        if(!tasks.isEmpty()) {
            List<Callable> list = (List<Callable>) tasks;

            for(Callable callable : list) {
               FutureTask<T> futureTask = new FutureTask<T>(callable);
               resultList.add(futureTask);
               new Thread(futureTask).start();
            }
        }

        return resultList;
    }

    //TODO 获取线程池启停状态
    @Override
    public boolean isShutdown() {
        return this.isShutdown;
    }

    //TODO 线程池执行关闭 关闭内部维护线程，关闭线程工厂的线程任务队列中执行的线程，并且中断当前线程池所在的主线程
    @Override
    public void shutdown() {
        synchronized (this) {
            //TODO 关闭内部维护线程
            this.localThread.interrupt();

            //TODO 关闭线程工厂中的工作线程,按照活跃线程个数逐个关闭
            if(CustomDefaultThreadFactory.getQueue().size() > 0) {
                CustomDefaultThreadFactory.getQueue().forEach(threadTask -> {
                    //TODO 中断线程池首先中断线程池内工作线程的执行的任务，然后在执行工作线程的中断，分两步走，即首先停止线程自己执行的任务，而后在停止线程

                    //TODO 重置工作线程队列中所有内部线程执行的内部任务，设置其running标记为false，即工作线程不再执行InternalTask(内部任务)去从任务阻塞队列中获取任务进行执行
                    if(threadTask instanceof ThreadRunnable) {
                        ((ThreadRunnable) threadTask).getInternalRunnableTask().stop();
                        logger.info(LocalTime.formatDate(LocalDateTime.now()) + "当前线程 [ "+ ((ThreadRunnable) threadTask).getThread().getName() + " ] 已关闭本线程任务......");

                        //TODO 中断当前工作线程
                        ((ThreadRunnable) threadTask).getThread().interrupt();
                        logger.info(LocalTime.formatDate(LocalDateTime.now()) + "当前线程 [ "+ ((ThreadRunnable) threadTask).getThread().getName() + " ] 已完成中断......");
                    } else {
                        ((ThreadCallable) threadTask).getInternalCallableTask().stop();
                        logger.info(LocalTime.formatDate(LocalDateTime.now()) + "当前线程 [ "+ ((ThreadCallable) threadTask).getThread().getName() + " ] 已关闭本线程任务......");

                        //TODO 中断当前工作线程
                        //((ThreadCallable) threadTask).getThread().interrupt();
                        //logger.info(LocalTime.formatDate(LocalDateTime.now()) + "当前线程 [ "+ ((ThreadCallable) threadTask).getThread().getName() + " ] 已完成中断......");
                    }

                });
            }

            //TODO 关闭线程池
            if(!this.isShutdown) {
                this.isShutdown = true;
                Thread.currentThread().interrupt();
            }

        }
    }

    public String getThreadPoolName() {
        return threadPoolName;
    }

    public void setThreadPoolName(String threadPoolName) {
        this.threadPoolName = threadPoolName;
    }

    public List<FutureTask<Callable>> getList() {
        return list;
    }

    public void setList(List<FutureTask<Callable>> list) {
        this.list = list;
    }
}
